<template>
  <div>
    <div class="header">
      <el-link type="primary">DTModeling</el-link>
      <div class="header-button">
        <el-button size="small" type="primary" @click="handleHelp">帮助指引</el-button>

        <el-dialog title="帮助文档" :visible.sync="helpDialogVisible" width="70%">
          <el-tabs tab-position="left" style="height:100%;" @tab-click="handlerScroll">
            <el-tab-pane :label="item.title" v-for="item in helpData" :key="item.id" v-html="item.content" style="height: 500px;overflow-y:scroll"></el-tab-pane>
          </el-tabs>
        </el-dialog>

        <el-button size="small" type="primary" @click="handleIn">导入JSON</el-button>
        <input ref="file" type="file" accept=".json" style="display: none" @change="fileDisplay" />
        <el-button size="small" type="primary" @click="handleOut">导出JSON</el-button>
        <el-button size="small" type="primary" @click="handleOutPng">导出PNG</el-button>
      </div>
    </div>
    <el-row class="menu-button">
      <el-tooltip class="item" effect="dark" content="保存" placement="bottom">
        <el-button @click="handleSaveToLocal" class="iconfont">&#xe601;</el-button>
      </el-tooltip>
      <el-tooltip class="item" effect="dark" content="清空" placement="bottom">
        <el-button @click="handleClearAllData" class="iconfont">&#xe66c;</el-button>
      </el-tooltip>
      <el-tooltip class="item" effect="dark" content="全选" placement="bottom">
        <el-button @click="handleSelectAll" class="iconfont">&#xe9c5;</el-button>
      </el-tooltip>
      <el-tooltip class="item" effect="dark" content="撤销" placement="bottom">
        <el-button @click="handleUndo" class="iconfont">&#xe64a;</el-button>
      </el-tooltip>
      <el-tooltip class="item" effect="dark" content="重做" placement="bottom">
        <el-button @click="handleRedo" class="iconfont">&#xe636;</el-button>
      </el-tooltip>
      <div class="zoomRegion">
        <el-tooltip class="item" effect="dark" content="放大" placement="bottom" >
          <el-button @click="handleZoomIn" class="iconfont" :disabled="disableZoomIn">&#xec14;</el-button>
        </el-tooltip>
        <span class="zoom">{{ zoom }}%</span>
        <el-tooltip class="item" effect="dark" content="缩小" placement="bottom">
          <el-button @click="handleZoomOut" class="iconfont" :disabled="disableZoomOut">&#xec13;</el-button>
        </el-tooltip>
      </div>
      <el-button @click="handleNew" type="info">新建Artifact类</el-button>
    </el-row>
    <el-dialog title="新建Artifact类" :visible.sync="newDialogVisible" width="50%">
      <span slot="footer" class="dialog-footer">
        <el-form ref='form' :model="form" label-width="80px">
          <el-form-item label="标签">
            <el-input v-model="form.label"></el-input>
          </el-form-item>
          <el-form-item label="属性">
            <div class="tag-list">
            <el-tag :key="attr" v-for="attr in form.attributeList" closable @close="handleAttrClose(attr)">{{attr}}</el-tag>
            <el-input class="input-new-tag" v-if="attrInputVisible" v-model="attrInputValue" ref="saveTagAttrInput" size="small" @keyup.enter.native="handleAttrInput" @blur="handleAttrInput"></el-input>
            <el-button v-else class="button-new-tag" size="small" @click="showAttrInput">+ New Attribute</el-button>              
            </div>
          </el-form-item>
          <el-form-item label="生命周期">
            <div class="tag-list">
            <el-tag :key="state" v-for="state in form.stateList" closable @close="handleStateClose(state)">{{state}}</el-tag>
            <el-input class="input-new-tag" v-if="stateInputVisible" v-model="stateInputValue" ref="saveTagStateInput" size="small" @keyup.enter.native="handleStateInput" @blur="handleStateInput"></el-input>
            <el-button v-else class="button-new-tag" size="small" @click="showStateInput">+ New State</el-button>              
            </div>
          </el-form-item>
        </el-form>
        <el-button @click="newDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="handleArtifact">新 建</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import eventBus from "../EventBus";
export default {
  name: "Menu",
  data() {
    return {
      zoom: 100,
      helpDialogVisible: false,
      newDialogVisible: false,
      attrInputValue:'',
      stateInputValue:'',
      attrInputVisible:false,
      stateInputVisible:false,
      form: {label:'', attributeList:[], stateList:[]},
      helpData: [
        {
          id: 1,
          title: "项目介绍",
          content:
            "<h2>背景</h2>"+
            "<p>在工业互联网领域，工厂懂工艺和机械知识但没有编程能力，互联网企业擅长系统落地但缺乏制造业专业能力，这成为工业互联网领域的一大痛点。数字孪生旨在完成物理世界与虚拟世界的映射，是工业互联网领域的先进技术。</p>"+
            "<h2>简介</h2>"+
            "<p>基于Antv X6图编辑引擎的数字孪生概念建模工具，Artiflow模型可以帮助业务人员以数据为中心建立业务流程模型，数字孪生实现物理世界与虚拟空间的数据交互，提供了无限可能，而以数据为中心的Artiflow业务流程模型非常适用于数字孪生概念建模。</p>",
        },
        { 
          id: 2, 
          title: "模型原理", 
          content: 
            "<h2>原理简介</h2>"+
            "通过库元素、服务、连接和事件描述关键数据Artifact从创建到存档的完整生命周期。其中Artifact是强类型的，库元素存储生命周期某一阶段的Artifact，服务读写Artifact，事件携带消息通知普通服务运行或者循环服务一直运行，服务完成后可能会产生一个新的消息。"+
            "<h2>Artiflow样例图</h2>"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/ArtiflowDemo.png' style='width:100%'></image>"+
            "<h2>Artiflow模型的形式化语言表述</h2>"+
            "<ul><li>Artiflow = (Artifacts, Services, Connections, Events)</li><li>Artifact = ({(a1, t1), (a2, t2), ...,(an, tn)}, {s1, s2, ..., sm})，Artifact是业务中的数据，表示该数据有m个生命周期和n个属性，每个属性都有类型</li>"+
            "<li>Service = {(a1, s1, t1), (a2, s2, t2), ..., (an, sn, tn)}，Service是操作数据的服务，该服务对若干个Artifact操作，操作类型有只读、引用读和写入，其中ai是操作的Artifact，si是操作的Artifact的生命周期，ti是操作类型</li>"+
            "<li>Repository被用来存储某一阶段的Artifact，用来存储(ai, ti)，其中ai是操作的Artifact，si是操作的Artifact的生命周期</li><li>Connection连接Service与Repository的管道，这通常伴随着对数据流的操作权限与缓存</li>"+
            "<li>Event = (producer, consumer, message),Event推动着Service执行，有的Service是事件生产者，有的Service是事件的消费者，事件着携带消息数据。如果一个服务如果没有事件推动它执行，那么它一定是自循环服务，否则这个服务永远不会执行是没有意义的。</li>"+
            "<li>Artiflow模型蕴含着业务规则，即“携带着消息的事件推动服务从一个库取出数据处理后放入另一个库，同时产生另一个事件”</li></ul>"
        },
        { 
          id: 3, 
          title: "绘图步骤", 
          content: 
            "<h2>1. 创建Artifact类</h2>"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/3.jpg' style='width:100%'></image>"+
            "<h2>2. 创建库元素</h2>"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/4.jpg' style='width:100%'></image>"+
            "<h2>3. 创建服务元素</h2>"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/5.jpg' style='width:100%'></image><hr />"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/6.jpg' style='width:100%'></image>"+
            "<h2>4. 创建事件</h2>"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/7.jpg' style='width:100%'></image><hr />"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/8.jpg' style='width:100%'></image>"+
            "<h2>5. 连线</h2>"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/9.jpg' style='width:100%'></image>"+
            "<h2>6. 创建Artifact</h2>"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/10.jpg' style='width:100%'></image>"
        },
        { 
          id: 4, 
          title: "体验优化", 
          content: 
            "<h2>1. 导入导出</h2>"+
            "<p>可以导出PNG和JSON格式，并且JOSN格式文件可以导入</p>"+ 
            "<h2>2. 保存和清空</h2>"+
            "<p>我们将数据保存在localstorage里，并且可以一键清空数据</p>"+ 
            "<h2>3. 缩放、撤销恢复、全选</h2>"+
            "<p>我们设计了若干个图标按钮方便用户绘图</p>"+ 
            "<h2>4. 快捷键</h2>"+
            "<p>ctrl c+v复制粘贴图元、ctrl a全选、ctrl z+shift z撤销恢复、delete删除。同时我们对mac电脑进行了适配</p>"
        },
        { 
          id: 5, 
          title: "项目架构与难点", 
          content: 
            "<h2>项目架构</h2>"+
            "<image src='https://image-1258128239.cos.ap-nanjing.myqcloud.com/11.jpg' style='width:100%'></image>"+ 
            "<h2>项目难点</h2>"+
            "<ul><li>模型复杂，组件间的数据通信有几十种，为此我们选用EventBus这种方式</li><li>各种组件要依据不同的事件响应式地发生变化，所以需要一个合理的代码结构，这样也方便多人合作开发</li><li>各种属性需要挂载到不同对象，依据数组的函数式编程需要一定的Javascript知识基础</li></ul>"
        },
        { id: 6, 
          title: "参与人员", 
          content: 
            "<h2>作者</h2>"+
            "<p>李纪奇（组长）、林智超、钱海蓬、郑成涛、桂俊毅</p>"+
            "<h2>特别鸣谢</h2>"+
            "<p>蚂蚁金服 文瑀</p>"+
            "<p>感谢蚂蚁金服举办的首届支付宝前锋营进阶班，许多优秀的前端框架开发者直播授课，为我们体系化地讲解前端前沿技术。同时还为每个组设置了指导老师，这里特别感谢Antv X6图编辑引擎的作者文瑀老师耐心为我们解答问题，也感谢Antv X6的全体开源作者为我们带来如此优秀的框架！</p>"
        },
      ],
    };
  },
  computed: {
    disableZoomIn() {
      return this.zoom < 300 ? false : true;
    },
    disableZoomOut() {
      return this.zoom > 50 ? false : true;
    },
  },
  methods: {

    // 帮助指引右侧滑块归位
    handlerScroll(tab) {
      // 获取滑块
      const scroll = tab.$parent.$children[0].$children[0].$el;
      let offset = parseInt(tab.index) * 60;
      setTimeout(() => {
        scroll.style.transform = `translateY(${offset}px)`;
      }, 0);
    },
    handleHelp(e) {
      this.helpDialogVisible = true;
      this.handleAutoBlur(e);
    },
    handleNew(e) {
      this.newDialogVisible = true;
      this.handleAutoBlur(e);
    },
    handleAttrClose(attr) {
        this.form.attributeList.splice(this.form.attributeList.indexOf(attr), 1);
    },
    showAttrInput() {
      this.attrInputVisible = true
      this.$nextTick(() => {
        this.$refs.saveTagAttrInput.$refs.input.focus()
      })
    },
    handleAttrInput() {
      if(this.attrInputValue) {
        if(this.attrInputValue.indexOf(':')===-1 || this.attrInputValue.split(':').length!=2) {
          this.$notify({title: "错误",message: "属性需要以属性:类型格式输入！",type: "error",offset: 50,});
        }
        else if(this.form.attributeList.some(item => item.split(':')[0] === this.attrInputValue.split(':')[0])) {
          this.$notify({title: "错误",message: "属性重复！",type: "error",offset: 50,});
        }
        else this.form.attributeList.push(this.attrInputValue)
      }
      this.attrInputVisible = false
      this.attrInputValue = ''
    },
    handleStateClose(state) {
        this.form.stateList.splice(this.form.stateList.indexOf(state), 1);
    },
    showStateInput() {
      this.stateInputVisible = true
      this.$nextTick(() => {
        this.$refs.saveTagStateInput.$refs.input.focus()
      })
    },
    handleStateInput() {
      if(this.stateInputValue) {
        if(this.form.stateList.includes(this.stateInputValue)) {
      this.$notify({
        title: "错误",
        message: "重复生命周期阶段！",
        type: "error",
        offset: 50,
      });
        }
        else this.form.stateList.push(this.stateInputValue)
      }
      this.stateInputVisible = false
      this.stateInputValue = ''
    },
    handleArtifact(){
      this.newDialogVisible = false
      eventBus.$emit('ArtifactClass:add', this.form)
      eventBus.$emit('Tree:update')
    },
    handleIn(e) {
      // document.getElementById("file").click();
      this.$refs.file.click();
      this.handleAutoBlur(e);
    },
    fileDisplay(e) {
      // const file = document.getElementById("file").files[0];
      const { 0: file } = e.target.files;
      const reader = new FileReader();
      reader.readAsText(file);
      reader.onload = function() {
        eventBus.$emit("fileIn", JSON.parse(this.result));
      };
      this.$notify({
        title: "成功",
        message: "导入JSON成功！",
        type: "success",
        offset: 50,
      });
    },
    handleOut(e) {
      eventBus.$emit("fileOut");
      this.$notify({
        title: "成功",
        message: "导出JSON成功！",
        type: "success",
        offset: 50,
      });
      this.handleAutoBlur(e);
    },
    handleOutPng(e) {
      eventBus.$emit("fileOutPng");
      this.$notify({
        title: "成功",
        message: "导出PNG成功！",
        type: "success",
        offset: 50,
      });
      this.handleAutoBlur(e);
    },
    handleSaveToLocal(e) {
      eventBus.$emit("saveToLocal");
      this.$notify({
        title: "成功",
        message: "保存成功！",
        type: "success",
        offset: 50,
      });
      this.handleAutoBlur(e);
    },
    handleClearAllData(e) {
      eventBus.$emit("clearAllData");
      this.$notify({
        title: "成功",
        message: "清空成功！",
        type: "success",
        offset: 50,
      });
      this.handleAutoBlur(e);
    },
    handleSelectAll(e) {
      eventBus.$emit("selectAll");
      this.$notify({
        title: "成功",
        message: "全选成功！",
        type: "success",
        offset: 50,
      });
      this.handleAutoBlur(e);
    },
    handleZoomIn(e) {
      eventBus.$emit("zoomIn");
      eventBus.$on("zoomGet",(msg)=> {
        this.zoom = Math.round(msg * 100);
      });
      this.handleAutoBlur(e);
    },
    handleZoomOut(e) {
      eventBus.$emit("zoomOut");
      eventBus.$on("zoomGet",(msg)=> {
        this.zoom = Math.round(msg * 100);
      });
      this.handleAutoBlur(e);
    },
    handleUndo(e) {
      eventBus.$emit("undo");
      this.handleAutoBlur(e);
    },
    handleRedo(e) {
      eventBus.$emit("redo");
      this.handleAutoBlur(e);
    },
    handleAutoBlur(evt) {
      let target = evt.target;
      if (target.nodeName === "SPAN" || target.nodeName === "button") {
        target = evt.target.parentNode;
      }
      target.blur();
    },
  },
  mounted() {
    document.addEventListener('keydown',(e)=> {
      if (e.key === 'Control') {
        eventBus.$emit("zoomListen");
        eventBus.$on("zoomGet",(msg)=> {
          this.zoom = Math.round(msg * 100);
      });
      }
    })
  }
};
</script>

<style scoped>
@font-face {
  font-family: "iconfont"; /* Project id 3264373 */
  src: url("//at.alicdn.com/t/font_3264373_p77tugnlhp.woff2?t=1647930561916") format("woff2"),
    url("//at.alicdn.com/t/font_3264373_p77tugnlhp.woff?t=1647930561916") format("woff"),
    url("//at.alicdn.com/t/font_3264373_p77tugnlhp.ttf?t=1647930561916") format("truetype");
}
.iconfont {
  font-family: "iconfont" !important;
  font-size: 18px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-weight: bold;
}
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 20px;
  height: 48px;
  border-bottom: 1px solid rgba(200, 200, 200, 0.75);
}
.el-link {
  font-weight: 500;
  font-size: 1.5rem;
}
.header-button {
  display: flex;
}
.header-button .el-button {
  margin-left: 20px;
}
.menu-button {
  text-align: left;
  border-bottom: 1px solid rgba(200, 200, 200, 0.75);
}
.menu-button .el-button {
  padding: 8px 8px !important;
  margin: 5px !important;
  border: 0px;
}
.el-button {
  transition: none !important;
}
.zoom {
  font-size: 14px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
    "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  color: rgba(0, 0, 0, 0.85);
}
.zoomRegion {
  display: inline-block;
  border-left: 1px solid rgba(200, 200, 200, 0.75);
  border-right: 1px solid rgba(200, 200, 200, 0.75);
}
.el-tabs--left ::v-deep .el-tabs__item.is-left {
  text-align: left;
}

.el-tabs--left ::v-deep .el-tabs__item.is-left:nth-child(n + 3) {
  text-align: left;
  margin-top: 20px;
}
.tag-list {
  display: flex;
  flex-wrap: wrap;
}
  .el-tag {
    margin-right: 10px;
    margin-top: 10px;
  }
  .button-new-tag {
    margin-top: 10px;
    margin-right: 10px;
    height: 32px;
    line-height: 30px;
    padding-top: 0;
    padding-bottom: 0;
  }
  .input-new-tag {
    width: 90px;
    margin-top: 5px;
    margin-right: 10px;
    /* vertical-align: bottom; */
  }
</style>
